import { Link, RepoLink } from '@brillout/docpress'
import { UiFrameworkExtension } from '../../components'

The `data()` hook is used for fetching data.

See <Link href="/data-fetching" /> for an introduction about `data()` and data fetching in general.

It's usually used with <Link href="/useData">`useData()`</Link>.

> For a lower-level hook with more control, see <Link href="/onBeforeRender" />.


## Examples

React + JavaScript:

- <Link href='/boilerplates/boilerplate-react/pages/star-wars/index/+data.js' />
- <Link href='/boilerplates/boilerplate-react/pages/star-wars/index/+Page.jsx' /> (`useData()` usage)
- <Link href='/boilerplates/boilerplate-react/pages/star-wars/@id/+data.js' />
- <Link href='/boilerplates/boilerplate-react/pages/star-wars/@id/+Page.jsx' /> (`useData()` usage)

React + TypeScript:

- <Link href='/boilerplates/boilerplate-react-ts/pages/star-wars/index/+data.ts' />
- <Link href='/boilerplates/boilerplate-react-ts/pages/star-wars/index/+Page.tsx' /> (`useData()` usage)
- <Link href='/boilerplates/boilerplate-react-ts/pages/star-wars/@id/+data.ts' />
- <Link href='/boilerplates/boilerplate-react-ts/pages/star-wars/@id/+Page.tsx' /> (`useData()` usage)

Vue + JavaScript:

- <Link href='/boilerplates/boilerplate-vue/pages/star-wars/index/+data.js' />
- <Link href='/boilerplates/boilerplate-vue/pages/star-wars/index/+Page.vue' /> (`useData()` usage)
- <Link href='/boilerplates/boilerplate-vue/pages/star-wars/@id/+data.js' />
- <Link href='/boilerplates/boilerplate-vue/pages/star-wars/@id/+Page.vue' /> (`useData()` usage)

Vue + TypeScript:

- <Link href='/boilerplates/boilerplate-vue-ts/pages/star-wars/index/+data.ts' />
- <Link href='/boilerplates/boilerplate-vue-ts/pages/star-wars/index/+Page.vue' /> (`useData()` usage)
- <Link href='/boilerplates/boilerplate-vue-ts/pages/star-wars/@id/+data.ts' />
- <Link href='/boilerplates/boilerplate-vue-ts/pages/star-wars/@id/+Page.vue' /> (`useData()` usage)


## `pageContext`

<Link href="/pageContext">`pageContext`</Link> provides contextual information.

```js
// /pages/product/+data.js

export async function data(pageContext) {
  // pageContext properties commonly used in data()
  pageContext.routeParams.id
  pageContext.user.id
  pageContext.urlParsed.search.query
}
```

> `pageContext.user` is a custom `pageContext` property, see:
> - <Link href="/pageContext#custom" />
> - <Link href="/auth#integration" />


## TypeScript

See <Link href="/useData#typescript" doNotInferSectionTitle />.


## Error handling

If an error is thrown by `data()`, then Vike renders your <Link text="error page" href="/error-page" /> and there is usually nothing for you to do (beyond defining an error page `/pages/_error/+Page.js`).

But if you want a more precise error handling (such as showing an insightful error message to the user instead of some generic "Something went wrong"), then use <Link href="/render" text={<code>throw render()</code>}></Link> and/or <Link href="/redirect" text={<code>throw redirect()</code>}></Link>.

```js
// /pages/movies/+data.js
// Environment: server

export { data }

import fetch from 'node-fetch'
import { render, redirect } from 'vike/abort'

async function data(pageContext) {
  const { id } = pageContext.routeParams
  const response = await fetch(`https://star-wars.brillout.com/api/films/${id}.json`)

  if (response.status === 404) {
    // Tell the user what went wrong
    throw render(404, `Movie with ID ${id} doesn't exist.`)
    /* Or redirect the user:
    throw redirect('/movie/add') */
    /* Or render the movie submission form while preserving the URL:
    throw render('/movie/add') */
  }

  // ...
}
```

> `throw render('/movie/add')` is a technique explained at <Link href="/auth#login-flow" />.

Alternatively, you can use `throw render()` and `throw redirect()` inside of a <Link href="/guard">`guard()` hook</Link>, see <Link href="/auth" />.


## Server-side

By default, the `data()` hook always runs the server-side. Thus you can directly use ORM/SQL database queries:

```js
// /pages/movies/+data.js

export { data }

// Note how we use `node-fetch`; this file is only run on the server-side, thus we don't need
// to use an isomorphic (aka universal) implementation such as `cross-fetch`.
import fetch from 'node-fetch'

async function data(pageContext) {
  const response = await fetch('https://star-wars.brillout.com/api/films.json')
  let { movies } = await response.json()
  /* Or with an ORM:
  let movies = await Movie.findAll() */
  /* Or with SQL:
  let movies = await sql.run('SELECT * FROM movies;') */

  // `movies` is serialized and passed to the client. Therefore, we pick only the
  // data the client needs in order to minimize what is sent over the network.
  movies = movies.map(({ title, release_date }) => ({ title, release_date }))

  return {
    movies
  }
}
```


## Client-side

By default, the `data()` hook always runs on the server-side.

But,
by using the <Link href="/file-env">file extension `.shared.js`</Link> (renaming `+data.js` to `+data.shared.js`),
you can tell Vike to run `data()` also on the client-side. This will make Vike:
- Call `data()` on the server-side for the first page the user visits.
- Call `data()` on the client-side for subsequent page navigations.

> In general, we recommend running `data()` only on the server-side because it's usually easier to write code that runs only on the server-side.
>
> That said, if you want to minimize requests made to your server, then it can make sense to run `data()` on the client-side. See <Link href="/pageContext.json#avoid-pagecontext-json-requests" doNotInferSectionTitle noBreadcrumb />.

You can also set `.client.js` instead of `.shared.js`: Vike will then always call `data()` on the client-side (never on the server-side).

> Alternatively, instead of using `.shared.js` and `.client.js`, you can modify the <Link href="/meta#example-modify-data-env">`meta.env` setting</Link> of the `data()` hook in a global fashion, removing the need to add `.shared.js` / `.client.js` to each of your `+data.js` files.


## `pageContext.data`

The `data()` hook sets the value of `pageContext.data`. (While `useData()` exposes the value of `pageContext.data`.)

This means that, beyond using <Link href="/useData">`useData()`</Link>, you can also access the data in other hooks over `pageContext.data`.


## Without `useData()`

The `data()` hook is usually used together with the component hook <Link href="/useData">`useData()`</Link> which is provided by the <UiFrameworkExtension />.

In general, for improved DX, we recommend using `data()` together with a `useData()` implementation.

> In case you don't use <UiFrameworkExtension name noLink />, you can implement `useData()` yourself as shown at <Link href="/useData#without-vike-react-vue-solid" doNotInferSectionTitle />

That said, you can also use `data()` without `useData()`:

```js
// /renderer/+onRenderHtml.js
// Environment: server

export { onRenderHtml }

import { escapeInject, dangerouslySkipEscape } from 'vike/server'
import { renderToHtml, createElement } from 'some-ui-framework'

async function onRenderHtml(pageContext) {
  // The data is available at pageContext.data
  const { Page, data } = pageContext
  const pageHtml = await renderToHtml(
    // Pass pageContext.data to the <Page> component
    createElement(Page, data)
  )
  /* JSX:
  const pageHtml = await renderToHtml(<Page {...data} />)
  */

  return escapeInject`<html>
    <div id='view-root'>
      ${dangerouslySkipEscape(pageHtml)}
    </div>
  </html>`
}
```

```js
// /renderer/+onRenderClient.js
// Environment: browser

export { onRenderClient }

import { hydrateDom, createElement } from 'some-ui-framework'

async function onRenderClient(pageContext) {
  const { Page, data } = pageContext
  await hydrateDom(
    // Pass pageContext.data to the <Page> component
    createElement(Page, data),
    document.getElementById('view-root')
  )
  /* JSX:
  await hydrateDom(<Page {...data} />, document.getElementById('view-root'))
  */
}
```

```js
// /pages/movies/+Page.js
// Environment: browser and server

export { Page }

// In the onRenderHtml() and onRenderClient() hooks above,
// pageContext.data is passed to the <Page> component.
function Page(data) {
  const { movies } = data
  // ...
}
```


## See also

- <Link href="/data-fetching" />
- <Link href="/useData" />
- <Link href="/onBeforeRender" />
- <Link href="/pageContext.json" />
